// CSE571 - dealer-svc-handler.cpp
// Authors: Jonathan Wald, Daniel Sarfati, Guangle Fan  
// Dealer service handler which is used (with an ACCEPT mask) for opening a
// connection and then (with the R/W masks) for managing each socket connection
// reactively.

#include <iostream>
#include <sstream>
#include <algorithm>

#include "ace/Reactor.h"
#include "ace/Svc_Handler.h"
#include "ace/Acceptor.h"
#include "ace/Synch.h"
#include "ace/SOCK_Acceptor.h"

#include "globals.h"
#include "dealer.h"
#include "game.h"
#include "player.h"
#include "dealer-svc-handler.h"
#include "io-manager.h"
#include "msg-flag.h"

const int TIMEOUT_VALUE = 2;
const int MAX_PLAYERS = 10;
const unsigned int NUM_CARDS = 5;

struct print_and_notify
  : std::unary_function<std::pair<string, Game*>, void> {
  void operator() (std::pair<string, Game*> pair ) {
    pair.second->printPlayersString();
  }
};

int DealerSvcHandler::open(void*){
  MsgFlag::icrNum();
  // This pointer is later stored within the deal and the memory is cleaned
  // up by the dealer.
  Player* new_player = new Player();
   
  // Assign this handle to the player's handler and give that handler access
  // to the player that uses it.
  new_player->handler.set_handle(get_handle());
  new_player->handler.player = new_player;

  new_player->handler.peer().disable(ACE_NONBLOCK);
  ACE_Reactor::instance()->register_handler(&(new_player->handler),
					    ACE_Event_Handler::READ_MASK);

  return 0;
}

int DealerSvcHandler::handle_input(ACE_HANDLE){
  if (DEBUG) std::cout << "HANDLING INPUT" << std::endl;
  IOManager::instance()->enqueue(this, 0);
  ACE_Reactor::instance()->mask_ops(this,
				    ACE_Event_Handler::READ_MASK,
				    ACE_Reactor::CLR_MASK);
  return 0;
}

int DealerSvcHandler::handle_completed_read(string data) {
  
  Dealer* dealer = Dealer::instance();
  if (data ==  CLIENT_QUIT_MSG) {
    if (DEBUG) {
      std::cout << "Handling CLIENT_QUIT_MSG. " << std::endl;
    }
    //sent_act = true;
    // Check to see that the game we want to quit is even
    // running anymore.
    counterpart_quit = true;
    if (player->game) {
      player->game->player_quit (player);
    }
    return 0;
  }

  // Put add_mask behind msg_handling to prevent segment fault due to
  // handling input after removing handler
  ACE_Reactor::instance ()->mask_ops(this,
  				     ACE_Event_Handler::READ_MASK,
				     ACE_Reactor::ADD_MASK);

  if (player->name.empty()) {
    // Expect to receive player's name.  
    std::cout << "New player connected: " << data << std::endl;
    player->name = data;
    if(!(player->inserted)) {
      Dealer::instance()->players.insert(std::pair<string, Player*>
					 (player->name, player));
      player->inserted = true;
    }
    enqueue_string_for_output(dealer->name);
  } else if (player->game_name.empty()) {
    // Expect to receive games.
    player->game_name = data;
    return 0;
  } else if (!player->game_type_received) {
    player->game_type_received = true;
    std::map<string, Game*>::iterator iter;
      
    iter = dealer->games.find(player->game_name);
    if (iter == dealer->games.end()) {
      Game* game = new Game();
      player->game = game;
      game->name = player->game_name;
      game->label = game->name;
      if (data == "draw" && dealer->plays_draw) {
	game->type = Game::DRAW;
      } else if (data == "stud" && dealer->plays_stud) {
	game->type = Game::STUD;
      } else if (data == "holdem" && dealer->plays_holdem) {
	game->type = Game::HOLDEM;
	game->initTableCards ();
      } else {
	std::cout << "Notifying client " << player->name
                  << " that they requested the  unsupported game type: "
		  << data << "." << std::endl;  
	ACE_Reactor::instance ()->mask_ops(this, 
					   ACE_Event_Handler::WRITE_MASK |
					   ACE_Event_Handler::READ_MASK,
					   ACE_Reactor::CLR_MASK); 
        enqueue_string_for_output("");
        enqueue_string_for_output(REMOVE_HANDLER);
        
        return 0;
      }
       
      game->players.push_back(player);

      // If this is a restarting game, delete the record of the old one.
      iter = dealer->finished_games.find(player->game_name);
      if (iter != dealer->finished_games.end()) {
        game->suspended = iter->second->suspended;
        game->label = iter->second->label;
        delete iter->second;
	dealer->finished_games.erase(iter);
      }

      dealer->games.insert(std::pair<string, Game*>(game->name, game));
      player->game->new_player_added();
      
    } 
    else {
      player->game = iter->second;
      iter->second->players.push_back(player);
      player->game->new_player_added();
    }      
    // If the game we just joined is suspended, suspend ourselves.
    if (player->game->suspended) {
      suspend();
    }


    std::for_each(dealer->games.begin(), dealer->games.end(),
                  print_and_notify());
  } else if (player->game->type == Game::HOLDEM && player->hand.size() != NUM_CARDS) {
    //we are playing holdem and receiving final hand from player
    char s = data[0];
    char r = data[1];
    Card card;
    card.set_values(s, r);
    player->hand.add_card(card);
  } else if (player->game->type == Game::DRAW &&
	     !player->discarded_done) {
    // In this case, we still need to get more cards from the
    // player.
    if (data.empty()) {
      player->discarded_done = true;
      return 0;
    }
    char s = data[0];
    char r = data[1];
    Card card;
    card.set_values(s, r);
    for (unsigned int i = 0; i < player->hand.size(); ++i) {
      if (player->hand[i] == card) {
        player->hand.remove_card(i);
        player->game->add_card(card);
        break;
      }
    }
    player->game->send_card(player);

  } else {  // Receive a score from the player.
    int score = atoi(data.c_str());
    player->score = score;
    player->restarting = true;
    player->game->score_added();
  }
  return 0;
}

int DealerSvcHandler::handle_output(ACE_HANDLE){
  if (DEBUG)  std::cout << "Handling output." << std::endl;
  IOManager::instance()->enqueue(this, 1);
  ACE_Reactor::instance ()->mask_ops(this,
  		   		     ACE_Event_Handler::WRITE_MASK,
  		   		     ACE_Reactor::CLR_MASK); 
  return 0;
}

int DealerSvcHandler::handle_close(ACE_HANDLE) {
  shutdown();
  return 0;
}

bool DealerSvcHandler::ready_for_read() {
  if (player->restarting) {
    if (player->game == NULL) {
      // Game has completed and is ready to be restarted.
      player->restarting = false;
      return true;
    } else {
      // Wait for game to finish up.
      if (DEBUG) std::cout << "Not ready for read." << std::endl;
    return false;
    }
  }
  return true;
}

bool DealerSvcHandler::suspended(ACE_Message_Block* mb) {
 if (!player->suspended) {
    return false;
  }
  player->suspension_queue.enqueue(mb);
  // game->mask = ACE_Reactor::instance()->mask_ops(this, game->mask,
  // 					 ACE_Reactor::GET_MASK);
  return true;
}

int DealerSvcHandler::suspend(void) {
  player->suspended = true;
  player->mask = ACE_Reactor::instance()->mask_ops(this, player->mask,
  					 ACE_Reactor::GET_MASK);
  ACE_Reactor::instance()->mask_ops(this, ACE_Event_Handler::READ_MASK |
  				    ACE_Event_Handler::WRITE_MASK,
  					 ACE_Reactor::CLR_MASK);
  first_resume = NULL;
  suspend_block = (ACE_Message_Block*)-1;
  return 0;
}

int DealerSvcHandler::resume(void) {
  player->suspended = false;
  ACE_Reactor::instance()->mask_ops(this, player->mask,
  				    ACE_Reactor::SET_MASK);
  ACE_Message_Block* mb;
  
  while(player->suspension_queue.dequeue(mb) != -1) {
    if(first_resume == NULL) first_resume = mb;
    IOManager::instance()->putq(mb);
  }
  resume_block = (ACE_Message_Block*)-1;
  return 0;
}

bool DealerSvcHandler::last_act() {
  Dealer* dealer = Dealer::instance(); 
  std::map<string, Player*>::iterator player_iter;
  for(player_iter = dealer->players.begin();
      player_iter != dealer->players.end();
      ++player_iter) {
    if(player_iter->second->handler.sent_act == false) {
      return false;
    }
  }
  return true;
}
